package com.hxl.tech.gateway.core.util;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.hxl.tech.gateway.common.constant.AppConstant;
import com.hxl.tech.gateway.common.constant.Result;
import com.hxl.tech.gateway.common.dto.ApplicationDTO;
import com.hxl.tech.gateway.common.dto.LimitIpDTO;
import com.hxl.tech.gateway.common.dto.LimitRuleDTO;
import com.hxl.tech.gateway.common.util.RedisUtil;
import com.hxl.tech.gateway.core.exception.GatewayException;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.stereotype.Component;
import java.net.URI;
import java.util.*;
import java.util.concurrent.TimeUnit;


@Slf4j
@Component
public class CacheManager {

    @SneakyThrows
    public Route get(String merchantCode, String appId, String apiName, String apiVersion) {

        String appKey = AppConstant.REDIS_APP_PREFIX + appId;
        Object app = RedisUtil.get(appKey);
        if(Objects.isNull(app)) {
            log.info("应用ID:[{}]不存在", appId);
            throw new GatewayException(Result.E10014);
        }


        ApplicationDTO applicationDTO = JSONObject.parseObject(app.toString(), ApplicationDTO.class);
        if (!applicationDTO.getState().equals(AppConstant.StateEnum.ENABLE)) {
            log.info("应用ID:[{}] 状态:[{}] 拒绝请求", appId, applicationDTO.getStateName());
            throw new GatewayException(Result.E10015);
        }

        String apiKey = AppConstant.REDIS_API_PREFIX + apiName;
        Object api = RedisUtil.get(apiKey);
        if(Objects.isNull(api)) {
            log.info("开放接口:[{}]不存在", apiName);
            throw new GatewayException(Result.E10016);
        }


        String apiApprovalKey = AppConstant.REDIS_API_APPROVAL_PREFIX + appId;
        Object apiApproval = RedisUtil.get(apiApprovalKey);
        List<String> apiApprovals = Objects.nonNull(apiApproval) ? Arrays.asList(apiApproval.toString().split(",")) : new ArrayList<>(0);
        if(!apiApprovals.contains(apiName)) {
            log.info("开放接口:[{}]未被授权", apiName);
            throw new GatewayException(Result.E10021);
        }


        // 可以根据merchantCode替换访问地址实现访问独享应用
        String shareUrlKey = AppConstant.REDIS_OPEN_URL_PREFIX + AppConstant.RESOURCE_MERCHANT_DEFAULT;
        String privateUrlKey = AppConstant.REDIS_OPEN_URL_PREFIX + merchantCode;
        Object shareUrl = RedisUtil.get(shareUrlKey);
        Object privateUrl = RedisUtil.get(privateUrlKey);
        if (Objects.isNull(shareUrl)  && Objects.isNull(privateUrl)) {
            log.info("商户号:[{}]不存在可用开放平台地址", merchantCode);
            throw new GatewayException(Result.E10017);
        }

        // 组合uri
        String visitUrl = Objects.nonNull(privateUrl) ? privateUrl.toString() : shareUrl.toString();
        String uri = visitUrl + apiName + (Objects.nonNull(apiVersion) ? "/v"+apiVersion : "");
        log.info("网关转发开放平台地址："+uri);

        RouteDefinition definition = new RouteDefinition();
        definition.setId(apiName);
        definition.setUri(URI.create(uri));
        PredicateDefinition pd = new PredicateDefinition();
        definition.setPredicates(Collections.singletonList(pd));
        definition.setOrder(0);
        return Route.async(definition).asyncPredicate((serverWebExchange -> (e) -> {
        })).build();
    }


    public boolean checkIpLimit(String ip) {

        String key = AppConstant.REDIS_LIMIT_IP_RULE;
        Object obj = RedisUtil.get(key);
        if (Objects.isNull(obj)) {
            return false;
        }

        LimitIpDTO limitIp = JSONObject.parseObject(obj.toString(), LimitIpDTO.class);
        if (limitIp.getLimitType().equals(0)) {
            return false;
        } else if (limitIp.getLimitType().equals(1) && limitIp.getWhiteIps().contains(ip)) {
            return false;
        }  else if (limitIp.getLimitType().equals(2) && !limitIp.getBlackIps().contains(ip)) {
            return false;
        }

        return true;
    }

    public List<LimitRuleDTO> getRateLimit(String appId, String merchantCode, String apiName) {

        List<LimitRuleDTO> limitRuleDTOS = new ArrayList<>();
        Set<String> keys = RedisUtil.getKeys(AppConstant.REDIS_LIMIT_RATE_RULE + "*");
        for (String key : keys) {
            String ruleDetail = "_" + appId + "_" + merchantCode + "_" + apiName;
            if (key.contains(ruleDetail)) {
                LimitRuleDTO limitRuleDTO = JSONObject.parseObject(RedisUtil.get(key).toString(), LimitRuleDTO.class);
                limitRuleDTOS.add(limitRuleDTO);
            }
        }
        return limitRuleDTOS;
    }

}
